
using System.ComponentModel;
using System;
using System.Collections;
using System.Diagnostics;
using System.IO;
using System.Reflection;

using CIndicators;
using CStock;
using CUtil;
using System.Collections.Generic;

namespace CStock
{
	/// <summary>
	/// this class is abstract mainly because of the ID property, which sets also the path,which has to be either 
	/// Daily or Intraday
	/// </summary>
    public abstract partial class DataClass : DataProvider, IDataClass
    {
      
        #region TradeEconomy Simulations properties
       
        [System.Xml.Serialization.XmlIgnoreAttribute()]
        public NameObjectCollection Equity
        {
            get
            {
                return this.equity;

            }
            set
            {
                this.equity = value;
            }
        }
        public double BondYield
        {
            get
            {
                DateTime forDate = ProgramContext.Instance.TradeEconomy.Date_Current;
                return CalculateBondYield(forDate);



            }

        }
        [System.Xml.Serialization.XmlIgnoreAttribute()]
        public NameObjectCollection EquityGrowthCollection
        {
            get
            {

                return CalculateGrowthRateCollection(this.equity);

            }

        }
        [System.Xml.Serialization.XmlIgnoreAttribute()]
        public NameObjectCollection EarningPerShareGrowthCollection
        {
            get
            {

                return CalculateGrowthRateCollection(this.earningPerShare);

            }

        }
        [System.Xml.Serialization.XmlIgnoreAttribute()]
        public NameObjectCollection CashFlowGrowthCollection
        {
            get
            {
                return CalculateGrowthRateCollection(this.cashFlow);

            }

        }
        [System.Xml.Serialization.XmlIgnoreAttribute()]
        public NameObjectCollection ReturnOfInvestmentGrowthCollection
        {
            get
            {

                return CalculateGrowthRateCollection(this.returnOfInvestment);

            }

        }
        [System.Xml.Serialization.XmlIgnoreAttribute()]
        public NameObjectCollection EarningPerShare
        {
            get
            {
                return this.earningPerShare;

            }
            set
            {
                this.earningPerShare = value;
            }
        }
        [System.Xml.Serialization.XmlIgnoreAttribute()]
        public NameObjectCollection CashFlow
        {
            get
            {
                return this.cashFlow;

            }
            set
            {
                this.cashFlow = value;
            }
        }






        [System.Xml.Serialization.XmlIgnoreAttribute()]
        public NameObjectCollection ReturnOfInvestment
        {
            get
            {
                return this.returnOfInvestment;

            }
            set
            {
                this.returnOfInvestment = value;
            }
        }
        public double PriceEarningPerShare
        {
            get
            {

                if (this.earningPerShare.Count <= 0 || this.QuoteCollection.Count <= 0)
                    return 0F;
                return this.QuoteCurrent / Convert.ToDouble(this.earningPerShare[this.earningPerShare.Count - 1].ToString());

            }

        }
        //[System.Xml.Serialization.XmlIgnoreAttribute()]
        public bool BondType
        {
            get
            {
                return this.bondType;

            }
            set
            {
                this.bondType = value;
            }
        }
        //[System.Xml.Serialization.XmlIgnoreAttribute()]
        public double BondInterestRate
        {
            get
            {
                return this.bondInterestRate;

            }
            set
            {
                this.bondInterestRate = value;
            }
        }
        //[System.Xml.Serialization.XmlIgnoreAttribute()]
        public double BondIssuePrice
        {
            get
            {
                return this.bondIssuePrice;

            }
            set
            {
                this.bondIssuePrice = value;
            }
        }
        public DateTime BondIssueDate
        {
            get
            {
                return this.bondIssueDate;

            }
            set
            {
                this.bondIssueDate = value;
            }
        }
        public DateTime BondExpiryDate
        {
            get
            {
                return this.bondExpiryDate;

            }
            set
            {
                this.bondExpiryDate = value;
            }
        }
        public double EquityGrowthLast
        {
            get
            {

                return CalculateGrowthRate(this.equity);


            }

        }
        public double EarningPerShareGrowthLast
        {
            get
            {

                return CalculateGrowthRate(this.earningPerShare);


            }

        }
        public double CashFlowGrowthLast
        {
            get
            {

                return CalculateGrowthRate(this.cashFlow);

            }

        }
        public double ReturnOfInvestmentGrowthLast
        {
            get
            {

                return CalculateGrowthRate(this.returnOfInvestment);


            }

        }
        [Category("Trade\\Orders"), DescriptionAttribute("ClassInstance")]
        [System.Xml.Serialization.XmlIgnoreAttribute()]
        public IOrders OrderStockBuy
        {
            get
            {
                return this.orderStockBuy;
            }
            set
            {
                this.orderStockBuy = value;

            }
        }
        [Category("Trade\\Orders"), DescriptionAttribute("ClassInstance")]
        [System.Xml.Serialization.XmlIgnoreAttribute()]
        public IOrders OrderStockSell
        {
            get
            {
                return this.orderStockSell;
            }
            set
            {
                this.orderStockSell = value;

            }
        }
        #endregion

        #region TradeEconomy Simulations - methods
        public bool UpdateOrderBook(DateTime myDateTime)
        {
            for (int i = this.orderStockBuy.Count - 1; i >= 0; i--)
            {
                IOrder myOrd = this.orderStockBuy[i] as IOrder;
                if (myOrd.DateDue <= myDateTime)
                {
                    this.orderStockBuy.Remove(myOrd);
                }
            }
            for (int i = this.orderStockSell.Count - 1; i >= 0; i--)
            {
                IOrder myOrd = this.orderStockSell[i] as IOrder;
                if (myOrd.DateDue <= myDateTime)
                {
                    this.orderStockBuy.Remove(myOrd);
                }
            }
            return true;

        }

        private void FindMinMaxOrder(ref double valMin, ref double valMax)
        {
            double valSellMax = -1;
            double valSellMin = 10000000;


            for (int i = this.orderStockSell.Count - 1; i >= 0; i--)
            {
                IOrder myOrd = this.orderStockSell[i] as IOrder;
                if (myOrd.Bid > valSellMax)
                    valSellMax = myOrd.Bid;
                if (myOrd.Bid < valSellMin)
                    valSellMin = myOrd.Bid;

            }
            double valBuyMax = -1;
            double valBuyMin = 10000000;
            for (int i = this.orderStockBuy.Count - 1; i >= 0; i--)
            {
                IOrder myOrd = this.orderStockBuy[i] as IOrder;
                if (myOrd.Bid > valBuyMax)
                    valBuyMax = myOrd.Bid;
                if (myOrd.Bid < valBuyMin)
                    valBuyMin = myOrd.Bid;

            }
            if (valBuyMax < valSellMax)
                valBuyMax = valSellMax;
            if (valBuyMin > valSellMin)
                valBuyMin = valSellMin;
            valMin = valBuyMin;
            valMax = valBuyMax;

        }
        private static double CheckSingleSellOrder(IOrder myOrd, double price)
        {
            double tradeVal = 0f;

            if (myOrd.Bid <= price)
            {
                tradeVal += price * myOrd.NumberOfItems;
            }
            return tradeVal;

        }
        private static double CheckSingleBuyOrder(IOrder myOrd, double price)
        {
            double tradeVal = 0f;
            if (myOrd.Bid >= price)
            {
                tradeVal += price * myOrd.NumberOfItems;
            }
            return tradeVal;

        }
        private double GetSellOrderBids(double price)
        {
            double tradeVal = 0;
            for (int i = 0; i < this.orderStockSell.Count; i++)
            {
                IOrder myOrd = this.orderStockSell[i] as IOrder;
                tradeVal += CheckSingleSellOrder(myOrd, price);
            }
            return tradeVal;
        }
        private double GetBuyOrderBids(double price)
        {
            double tradeVal = 0;
            for (int i = 0; i < this.orderStockBuy.Count; i++)
            {
                IOrder myOrd = this.orderStockBuy[i] as IOrder;
                tradeVal += CheckSingleBuyOrder(myOrd, price);


            }
            return tradeVal;
        }
        private bool CreateCollectionsForBuyAndSellOffers(ref NameObjectCollection valuesSell, ref NameObjectCollection valuesBuy, ref double fstep, double valMax, double valMin)
        {
            //create collection in the granularity "searchGranularity" with a total value of money 
            // for sell and buy bids
            valuesSell = new NameObjectCollection();
            valuesBuy = new NameObjectCollection();

            //1. explicitely investigate all buy and sell offer points 
            //so that we do not miss the points because of numerical reasons
            for (int i = 0; i < this.orderStockBuy.Count; i++)
            {
                IOrder ord = this.orderStockBuy[i] as IOrder;

                double priceFix = ord.Bid;
                double sellVal = GetSellOrderBids(priceFix);
                double buyVal = GetBuyOrderBids(priceFix);

                valuesSell.Add(priceFix, sellVal);
                valuesBuy.Add(priceFix, buyVal);


            }
            for (int i = 0; i < this.orderStockSell.Count; i++)
            {
                IOrder ord = this.orderStockSell[i] as IOrder;

                double priceFix = ord.Bid;
                double sellVal = GetSellOrderBids(priceFix);
                double buyVal = GetBuyOrderBids(priceFix);

                //if(!valuesSell.Keys.Contains[price])
                valuesSell.Add(priceFix, sellVal);
                valuesBuy.Add(priceFix, buyVal);


            }


            double searchGranularity = 100;


            fstep = (valMax - valMin) / searchGranularity;
            //for the case, valMax = valMin (therefor sell order = buy order bid)
            if (fstep == 0)
                fstep = 0.001F;

            double price = 0;
            for (price = valMin; price <= valMax; price += fstep)
            {
                double sellVal = GetSellOrderBids(price);

                double buyVal = GetBuyOrderBids(price);

                valuesSell.Add(price, sellVal);
                valuesBuy.Add(price, buyVal);



            }
            return true;
        }
        private double FindOptimalPrice()
        {
            //1. Sort orders
            ((IOrders)orderStockSell).SortAfterBid();
            ((IOrders)orderStockBuy).SortAfterBid();

            //2. Find min and max range
            double valMin = -1;
            double valMax = 10000000;

            FindMinMaxOrder(ref valMin, ref valMax);

            //create collection in the granularity "searchGranularity" with a total value of money 
            // for sell and buy bids
            NameObjectCollection valuesSell = null;
            NameObjectCollection valuesBuy = null;
            double fstep = 0;
            CreateCollectionsForBuyAndSellOffers(ref valuesSell, ref valuesBuy, ref fstep, valMax, valMin);
            return Find_Buy_Sell_Max(valuesSell, valuesBuy);


        }
        private static double Find_Buy_Sell_Max(NameObjectCollection valuesSell, NameObjectCollection valuesBuy)
        {
           
            double priceFound = 0;

            //goal of this routine:
            //Find the point where the sum of buy and sell = max

            NameObjectCollection collSum = new NameObjectCollection();
            for (int i = 0; i < valuesBuy.Count; i++)
            {
                double valBuy = Convert.ToDouble(valuesBuy[i]);
                double valSell = Convert.ToDouble(valuesSell[i]);
                if (valBuy <= valSell)
                    collSum.Add(valuesBuy.Keys[i], valBuy);
                else
                    collSum.Add(valuesBuy.Keys[i], valSell);


            }
            collSum.SortByValue();
            //first guess is
            object keyTrade = collSum.Keys[collSum.Count - 1];
            priceFound = Convert.ToDouble(keyTrade);

            Debug.WriteLine("Price is " + collSum.Keys[collSum.Count - 1] + "; expected trade at this point is " + collSum[collSum.Count - 1] + "; sell: " + valuesSell[keyTrade.ToString()] + "; buys " + valuesBuy[keyTrade.ToString()]);



            return priceFound;
        }
        private bool UpdateBidOnIndicatorOrders(IOrders myOrders)
        {

            for (int i = 0; i < myOrders.Count; i++)
            {
                IOrder myOrd = myOrders[i] as IOrder;

                if (!String.IsNullOrEmpty(myOrd.BidOnIndicator))
                {
                    IIndicator ind = Indicator.IndicatorEvaluate(myOrd.BidOnIndicator.ToString(), this);
                    Debug.Assert(ind.Values.Count > 0, "Indicator not evaluated " + ind.Name);
                    if (ind.Values.Count != 0)
                    {
                        //DateTime currentDate = ProgramContext.Instance.TradeEconomy.Date_Current;
                        double indval = ind.Values[ind.Values.Count - 1].CLOSEc;
                        if (indval > Convert.ToDouble(myOrd.BidOnIndicatorValue, CUtil.Conversions.CultureAmerican))
                        {
                            myOrd.Bid = this.QuoteCurrent;
                        }
                        else
                        {
                            //bid at 0 
                            myOrd.Bid = 0f;
                        }
                    }
                    //sellVal += myOrd.Bid * myOrd.NumberOfValues;
                    //ordersSellToExecute.Add(myOrd);
                }

            }
            return true;


        }
        private bool UpdateBidOnIndicatorOrders()
        {
            UpdateBidOnIndicatorOrders(this.orderStockSell);
            UpdateBidOnIndicatorOrders(this.orderStockBuy);

            return true;
        }
        public bool TradeStockOrders()
        {
            UpdateBidOnIndicatorOrders();

            double priceFound = FindOptimalPrice();

            //now we found a price ...
            //execute orders in the order of pricing...
            return TradeOrdersAtPrice(priceFound);

        }
        private bool CreateOrdersToExecuteCollections(double price, ref IOrders ordersSellToExecute, ref IOrders ordersBuyToExecute)
        {
            ordersSellToExecute = new Orders();
            ordersBuyToExecute = new Orders();
            double sellVal = 0;
            double buyVal = 0;
            for (int i = 0; i < this.orderStockSell.Count; i++)
            {
                IOrder myOrd = this.orderStockSell[i] as IOrder;
                if ((myOrd.Bid - price) < 0.001)
                {
                    sellVal += myOrd.Bid * myOrd.NumberOfItems;
                    ordersSellToExecute.Add(myOrd);
                }

            }


            for (int i = 0; i < this.orderStockBuy.Count; i++)
            {
                IOrder myOrd = this.orderStockBuy[i] as IOrder;
                if ((myOrd.Bid - price) < 0.001)
                {
                    buyVal += myOrd.Bid * myOrd.NumberOfItems;
                    ordersBuyToExecute.Add(myOrd);
                }
            }
            //3 cases - we have to adjust if the stocks to trade do not fit
            //split orders in the one which will be executed and the rest

            if (sellVal > buyVal)
            {
                //too much to sell
                for (int i = ordersSellToExecute.Count - 1; i >= 0; i--)
                {
                    //see difference - price which will be traded
                    //double diffSelBuy = sellVal - buyVal;

                    IOrder myOrd = ordersSellToExecute[i] as IOrder;
                    double valLoop = myOrd.Bid * myOrd.NumberOfItems;
                    double diffSellBuyLoop = sellVal - buyVal - valLoop;
                    if (diffSellBuyLoop < 0)
                    {
                        double fVal = sellVal - buyVal;
                        int myNewNumber = Convert.ToInt32(fVal / myOrd.Bid);
                        Order objOrder = myOrd as Order;
                        IOrder orderClone = objOrder.Clone() as IOrder;

                        //new order - containing number of traded items
                        orderClone.NumberOfItems = myNewNumber;

                        myOrd.NumberOfItems = myOrd.NumberOfItems - myNewNumber;
                        //order adjusted the number of items ...

                        //ordersSellToExecute.Remove(myOrd);
                        //ordersSellToExecute.Add(orderClone);
                        this.orderStockSell.Add(orderClone);
                        //We reached success!!
                        break;

                    }
                    else
                    {
                        ordersSellToExecute.Remove(myOrd);
                        sellVal -= myOrd.Bid * myOrd.NumberOfItems;

                    }

                }
            }
            if (sellVal < buyVal)
            {
                for (int i = ordersBuyToExecute.Count - 1; i >= 0; i--)
                {
                    //double diffSelBuy = buyVal - sellVal;

                    IOrder myOrd = ordersBuyToExecute[i] as IOrder;
                    double valLoop = myOrd.Bid * myOrd.NumberOfItems;
                    double diffSellBuyLoop = buyVal - sellVal - valLoop;
                    if (diffSellBuyLoop < 0)
                    {
                        double fVal = sellVal;
                        int myNewNumber = Convert.ToInt32(fVal / myOrd.Bid);
                        Order objOrder = myOrd as Order;
                        IOrder orderClone = objOrder.Clone() as IOrder;

                        orderClone.NumberOfItems = myNewNumber;

                        myOrd.NumberOfItems = myOrd.NumberOfItems - myNewNumber;

                        ordersBuyToExecute.Remove(myOrd);
                        ordersBuyToExecute.Add(orderClone);
                        this.orderStockBuy.Add(orderClone);
                        //We reached success!!
                        break;

                    }
                    else
                    {
                        ordersBuyToExecute.Remove(myOrd);
                        buyVal -= myOrd.Bid * myOrd.NumberOfItems;

                    }

                }
            }
            return true;
        }
        private bool TradeOrdersAtPrice(double price)
        {
            //now we found a price ...
            //execute orders in the order of pricing...
            //1. Get all orders sell which can be executed at this price

            IOrders ordersSellToExecute = null;
            IOrders ordersBuyToExecute = null;
            CreateOrdersToExecuteCollections(price, ref ordersSellToExecute, ref ordersBuyToExecute);


            return TradeOrdersHelper(ordersSellToExecute, ordersBuyToExecute, price);


        }
        private bool TradeOrdersHelper(IOrders ordersSellToExecute, IOrders ordersBuyToExecute, double price)
        {


            IDataItem di = this.Parent as DataItem;

            //int numberOfItems = 0;
            int tradedNumber = 0;
            for (int i = 0; i < ordersSellToExecute.Count; i++)
            {
                IOrder myOrd = ordersSellToExecute[i] as IOrder;
                this.orderStockSell.Remove(myOrd);
                ITrader myTraderSell = TradeEconomy.GetTraderByID(myOrd.TraderID);
                myTraderSell.TradeDataItem_Sell(this.iD, -myOrd.NumberOfItems, price);
                tradedNumber += myOrd.NumberOfItems;


            }
            for (int i = 0; i < ordersBuyToExecute.Count; i++)
            {
                IDataItem diClone = di.Clone() as IDataItem;
                diClone.Number = 0;
                IOrder myOrd = ordersBuyToExecute[i] as IOrder;
                this.orderStockBuy.Remove(myOrd);
                ITrader myTraderBuy = TradeEconomy.GetTraderByID(myOrd.TraderID);
                myTraderBuy.TradeDataItem_Buy(diClone, myOrd.NumberOfItems, price);
                tradedNumber += myOrd.NumberOfItems;

            }
            //update if lastTradedQuote for price and volume information (add new Quote) 
            if (tradedNumber > 0)
            {
                IQuote qlast = this.oHLC[this.oHLC.Count - 1] as IQuote;
                this.lastTradedQuote = new Quote(qlast);
                this.lastTradedQuote.CLOSEc = price;
                this.lastTradedQuote.VOLUME += tradedNumber;

            }
            return true;
        }
        public bool CreateNewQuote_Bond_FromInterestRate(DateTime dt, double myPercentage_InterestCB)
        {
            //formula
            // CurrentPrice = BondIssuePrice * BondInterestRate / Percentage_InterestCB

            double fPrice = this.bondInterestRate * this.bondIssuePrice / myPercentage_InterestCB - 0.05F;
            //the quote is a small amount less than the current expected rat
            //fPrice = fPrice - 0.05;

            IQuote quOld = this.oHLC[this.oHLC.Count - 1] as IQuote;
            this.lastTradedQuote = new Quote(quOld);

            IQuote quNew = new Quote(this.lastTradedQuote);

            quNew.DATE = dt;
            quNew.VOLUME = this.lastTradedQuote.VOLUME;

            quNew.HIGH = fPrice;
            quNew.OPENc = fPrice;
            quNew.LOW = fPrice;
            quNew.CLOSEc = fPrice;
            //this.lastTradedQuote = new Quote(quOld);
            //this.lastTradedQuote.DATE = dt;
            this.oHLC.Add(quNew);
            AddSingleQuoteToSeries(quNew);
            return true;


        }
        public bool CreateNewQuoteFromLastTradedPrice(DateTime dt)
        {
            if (this.lastTradedQuote != null && this.lastTradedQuote.VOLUME > 0)
            {
                IQuote quNew = new Quote(this.lastTradedQuote);
                quNew.DATE = dt;
                quNew.VOLUME = this.lastTradedQuote.VOLUME;

                //this.lastTradedQuote = new Quote(quOld);
                //this.lastTradedQuote.DATE = dt;
                this.oHLC.Add(quNew);
                AddSingleQuoteToSeries(quNew);
                this.lastTradedQuote = null;

                return true;
            }
            else
            {
                IQuote quOld = this.oHLC[this.oHLC.Count - 1] as IQuote;
                this.lastTradedQuote = new Quote(quOld);
                this.lastTradedQuote.DATE = dt;
                this.oHLC.Add(this.lastTradedQuote);
                AddSingleQuoteToSeries(this.lastTradedQuote);

                this.lastTradedQuote = null;


            }

            return false;


        }
        public double CalculateBondYield(DateTime forDate)
        {

            TimeSpan diff = this.bondExpiryDate - forDate;

            double fQuoteCurrent = this.QuoteCollection[forDate].CLOSEc;



            int days = diff.Days;

            double noOfYears = days / 365;
            double totalValueOfInterest = this.bondInterestRate * this.bondIssuePrice / 100 * noOfYears;

            double newYield = totalValueOfInterest / fQuoteCurrent * 100 / noOfYears;

            return newYield;
        }

        public NameObjectCollection CalculateGrowthRateCollection(NameObjectCollection myNameObjectCollection)
        {
            if (myNameObjectCollection.Count <= 0)
                return null;
            DateTime dtStart = Convert.ToDateTime(myNameObjectCollection.Keys[0].ToString());
            double valueStart = Convert.ToDouble(myNameObjectCollection[0].ToString());

            NameObjectCollection collResult = new NameObjectCollection();
            collResult.Add(dtStart, 0f);

            for (int i = 1; i < myNameObjectCollection.Count; i++)
            {
                DateTime dtEnd = Convert.ToDateTime(myNameObjectCollection.Keys[i].ToString());



                double valueEnd = Convert.ToDouble(myNameObjectCollection[i].ToString());

                TimeSpan diff = dtEnd - dtStart;
                int days = diff.Days;
                double Fyears = 1F / (days / 365);


                //formula is
                // ( (last / first) **(1/noOfYears) -1  ) * 100 (in percent)
                double growthRate = Convert.ToDouble(Math.Pow((valueEnd / valueStart), Fyears));
                growthRate = (growthRate - 1) * 100;

                collResult.Add(dtEnd, growthRate);
            }

            return collResult;
            //return CalculateGrowthRate(myNameObjectCollection, myDateEnd); 

        }
        private double CalculateGrowthRate(NameObjectCollection myNameObjectCollection)
        {
            if (this.bondType)
                return 0f;

            NameObjectCollection collResult = CalculateGrowthRateCollection(myNameObjectCollection);
            return Convert.ToDouble(collResult[collResult.Count - 1]);



        }
        #endregion

       

    }
}
